package service

import (
	"context"
	"errors"
	"fmt"
	"gRPC_Server/pbfiles"
	"github.com/jinzhu/copier"
	"log"
	"sync"
)

var ErrAlreadyExists = errors.New("record already exists")

type LaptopStore interface {
	Save(Laptop *pbfiles.Laptop) error
	Find(id string) (*pbfiles.Laptop, error)
	//搜索服务，返回一个函数回调
	Search(ctx context.Context, filter *pbfiles.Filter, found func(laptop *pbfiles.Laptop) error) error
}

type InMemoryLaptopStore struct {
	mutex sync.RWMutex //可能有并发访问，因此采用读写锁
	data  map[string]*pbfiles.Laptop
}

func NewInMemoryLaptopStore() *InMemoryLaptopStore {
	return &InMemoryLaptopStore{
		data: make(map[string]*pbfiles.Laptop),
	}
}
func (store *InMemoryLaptopStore) Save(laptop *pbfiles.Laptop) error {
	store.mutex.Lock() //并发访问加锁
	defer store.mutex.Unlock()

	if store.data[laptop.Id] != nil {
		return ErrAlreadyExists
	}

	//安全起见，进行深拷贝
	other := &pbfiles.Laptop{}
	err := copier.Copy(other, laptop)
	if err != nil {
		return fmt.Errorf("cannot copy laptop data: %w", err)
	}
	store.data[other.Id] = other
	return nil
}

func (store *InMemoryLaptopStore) Find(id string) (*pbfiles.Laptop, error) {
	store.mutex.RLock()
	defer store.mutex.RUnlock()

	laptop := store.data[id]
	if laptop == nil {
		return nil, nil
	}
	return deepCopy(laptop)
}
func (store *InMemoryLaptopStore) Search(
	ctx context.Context, filter *pbfiles.Filter,
	found func(laptop *pbfiles.Laptop) error) error {
	store.mutex.RLock()
	defer store.mutex.RUnlock()
	for _, laptop := range store.data {
		if ctx.Err() == context.Canceled || ctx.Err() == context.DeadlineExceeded { // 客户端出现超时，取消了请求等错误
			log.Print("context is cancelled")
			return nil
		}
		if isQualified(filter, laptop) {
			other, err := deepCopy(laptop)
			if err != nil {
				return err
			}
			err = found(other) //回调
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func isQualified(filter *pbfiles.Filter, laptop *pbfiles.Laptop) bool {
	if filter.GetMaxPriceUsd() < laptop.GetPriceUsd() {
		return false
	}
	if filter.GetMinCpuCores() > laptop.GetCpu().GetNumberCores() {
		return false
	}
	if filter.GetMinCpuGhz() > laptop.GetCpu().GetMinGhz() {
		return false
	}
	if toBit(filter.GetMinRam()) > toBit(laptop.GetMemory()) {
		return false
	}
	return true
}
func toBit(memory *pbfiles.Memory) uint64 {
	value := memory.GetValue()

	switch memory.GetUnit() {
	case pbfiles.Memory_BIT:
		return value
	case pbfiles.Memory_BYTE:
		return value << 3
	case pbfiles.Memory_KILOBYTE:
		return value << 13
	case pbfiles.Memory_MEGABYTE:
		return value << 23
	case pbfiles.Memory_GIGABYTE:
		return value << 33
	case pbfiles.Memory_TERABYTE:
		return value << 43
	default:
		return 0
	}
}
func deepCopy(laptop *pbfiles.Laptop) (*pbfiles.Laptop, error) {
	other := &pbfiles.Laptop{}
	err := copier.Copy(other, laptop)
	if err != nil {
		return nil, fmt.Errorf("cannot copy laptop data: %w", err)
	}
	return other, nil
}

type DBLaptopStore struct {
}
